#include "stdafx.h"
#include "Input.h"
#include <Windows.h>
#include "keys.h"
#include <assert.h>
#include "Controls.h"
#include <Windowsx.h>
#include "..\EdgeGameCore\Delegate.h"

#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "dxguid.lib")

Input* Input::instance = nullptr;

void Input::Init(HWND hwnd)
{
	instance = new Input;
	instance->hwnd = hwnd;
}

Input::Input(void)
{
	// load game controls from controls.ini
	Controls::GetInstance();

	/*******Inilitize key bindings*************************************************/
	key_bindings.emplace(Controls::GetInstance().move_forward, new Delegate<key_event_handler>);
	key_bindings.emplace(Controls::GetInstance().move_backward, new Delegate<key_event_handler>);
	key_bindings.emplace(Controls::GetInstance().move_left, new Delegate<key_event_handler>);
	key_bindings.emplace(Controls::GetInstance().move_right, new Delegate<key_event_handler>);
	key_bindings.emplace(Controls::GetInstance().open_inventory, new Delegate<key_event_handler>);
	key_bindings.emplace(Controls::GetInstance().jump, new Delegate<key_event_handler>);
	key_bindings.emplace(Controls::GetInstance().primary_fire, new Delegate<key_event_handler>);
	key_bindings.emplace(Controls::GetInstance().block, new Delegate<key_event_handler>);
	key_bindings.emplace(Controls::GetInstance().reload, new Delegate<key_event_handler>);
	key_bindings.emplace(Controls::GetInstance().sprint, new Delegate<key_event_handler>);
	key_bindings.emplace(Controls::GetInstance().interact, new Delegate<key_event_handler>);
	/*******Inilitize mouse bindings***********************************************/
	key_bindings.emplace(Controls::GetInstance().lock_on, new Delegate<mouse_event_handler>);
	mouse_bindings.emplace(Controls::GetInstance().primary_fire, new Delegate<mouse_event_handler>);
	mouse_bindings.emplace(Controls::GetInstance().block, new Delegate<mouse_event_handler>);

	// set up generic input events
	mouse_moved = new Delegate < mouse_event_handler > ;
	scrollWeel_up = new Delegate < mouse_event_handler > ;
	scrollWeel_down = new Delegate < mouse_event_handler > ;

	HRESULT hr = DirectInput8Create(GetModuleHandle(nullptr), DIRECTINPUT_VERSION, IID_IDirectInput8A, (void**)&direct_input, nullptr);
	// assert inilitization of direct input
	assert(SUCCEEDED(hr) && "Failed to inilitze input manager");

	direct_input->CreateDevice(GUID_SysMouse, &pMouse, nullptr);
	if (pMouse)
	{
		pMouse->SetDataFormat(&c_dfDIMouse);
		pMouse->Acquire();
	}
	else
		assert("Failed to inilitize mouse input!");
}


Input::~Input(void)
{
	// free key bindings
	for (auto i = key_bindings.begin(); i != key_bindings.end(); i++)
		if (i->second)
			delete i->second;
	// free mouse bindings
	for (auto i = mouse_bindings.begin(); i != mouse_bindings.end(); i++)
		if (i->second)
			delete i->second;

	// free generic input methods
	delete mouse_moved;
	delete scrollWeel_up;
	delete scrollWeel_down;
}

bool Input::isKeyDown(int keyCode)
{
	assert(keyCode > 0 && keyCode < 256 && "KeyCode cannot be non-negitive and less than 256!");
	short val = curr_buffer[keyCode];
	return (val & (1 << 15)) != 0;
}

bool Input::isKeyPressed(MouseButtons button)
{
	return isKeyDown(button) && (prev_buffer[(int)button] & (1 << 15)) == 0;
}

bool Input::isKeyDown(MouseButtons button)
{
	return isKeyDown((int)button);
}

bool Input::isKeyDown(Keys keycode)
{
	return isKeyDown((int)keycode);
}

bool Input::isKeyUp(Keys keycode)
{
	return isKeyUp((int)keycode);
}

bool Input::isKeyUp(int keyCode)
{
	return !isKeyDown(keyCode);
}

bool Input::isKeyPressed(Keys keyCode)
{
	return isKeyDown(keyCode) && !wasKeyDown(keyCode);
}

bool Input::isKeyUp(MouseButtons button)
{
	return !isKeyDown(button);
}
bool Input::wasKeyDown(Keys keycode)
{
	return (prev_buffer[(int)keycode] & (1 << 15)) != 0;
}

bool Input::wasKeyDown(MouseButtons button)
{
	return (prev_buffer[(int)button] & (1 << 15)) != 0;
}

void Input::BeginUpdate(void)
{
	DIMOUSESTATE mouseState;
	pMouse->Acquire();
	// get mouse data
	HRESULT hr = pMouse->GetDeviceState(sizeof(DIMOUSESTATE), &mouseState);

	// attempt to re-aquire mouse on get state failure
	if (FAILED(hr))
		pMouse->Acquire();

	// raise the mouse moved event if the mouse has moved
	if (mouseState.lX != 0 || mouseState.lY != 0)
	{
		long params[] =
		{
			mouseState.lX,
			mouseState.lY,
		};

		if (windowFocoused)
			mouse_moved->Invoke((void**)&params);
	}

	// update the current mouse position
	POINT cPos;
	GetCursorPos(&cPos);
	mouse_x = cPos.x;
	mouse_y = cPos.y;
	// keep mouse position relitive to the current window
	RECT windowRect;
	GetWindowRect(hwnd, &windowRect);
	mouse_x -= windowRect.left;
	mouse_y -= windowRect.top;

	for (int i = 0; i < 256; i++)
		curr_buffer[i] = GetAsyncKeyState(i);

	// call key pressed events
	for (auto i = key_bindings.begin(); i != key_bindings.end(); i++)
	{
		auto subscribers = i->second->subscribers;
		NamedKey key = (i->first);
		// invoke this functor if the key is down
		if (isKeyDown(key.key))
		{
			for (unsigned int i = 0; i < subscribers.size(); i++)
				subscribers[i](nullptr);
		}
	}

}
	 
void Input::EndUpdate(void)
{
	memcpy(prev_buffer, curr_buffer, sizeof(short) * 256);
}

char Input::GetAnyKeyDown(void) const
{
	
	// Check if any character-key is down
	for (short key = 254; key >= 0; key--)
	{
		if ((curr_buffer[key] & (1 << 15)) != 0)
			return char(key);
	}


	// No character-keys are down
	return 0;
}


void Input::setMousePosition(Vector2 position)
{
	SetCursorPos((int)position.x, (int)position.y);
}

void Input::setMousePosition(int x, int y)
{
	SetCursorPos(x, y);
}

void Input::WinProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	static int mouse_prev_x, mouse_prev_y, weel_val, weel_prev;

	switch (message)
	{
	case WM_MOUSEWHEEL:
	{
		weel_val = GET_WHEEL_DELTA_WPARAM(wParam);
		int prams[1];
		prams[0] = (weel_val > 0 ? 1 : -1);
		if (prams[0] > 0)
			(*scrollWeel_up)((void**)&prams);
		else
			(*scrollWeel_down)((void**)&prams);
		weel_prev = weel_val;
	}
	case WM_ACTIVATE:
		if ((wParam & 0xffffffff) == 0)
			windowFocoused = false;
		else
			windowFocoused = true;
		break;
	}
}